<?php
/**
 * A class is used to write php log 
 * 
 * @file Log.class.php
 * @date 2011/05/04 10:31:44
 * @version 1.0
 * @example
 <?php
 require_once('Log.class.php');

 $GLOBALS['LOG'] = array(
		"intLevel"			=> 0x07,		//fatal, warning, notice
		"strLogFile"		=> '/home/www/roblibs/log/roblibs.log',	//test.log.wf will be the wf log file
	);

 $str = 'log string';
 Log::debug($str);
 Log::trace($str);
 Log::notice($str);
 Log::warning($str);
 Log::fatal($str);
 *
 **/

class Log
{
    const LOG_LEVEL_NONE    = 0x00;
    const LOG_LEVEL_FATAL   = 0x01;
    const LOG_LEVEL_WARNING = 0x02;
    const LOG_LEVEL_NOTICE  = 0x04;
    const LOG_LEVEL_TRACE   = 0x08;
    const LOG_LEVEL_DEBUG   = 0x10;
    const LOG_LEVEL_ALL     = 0xFF;

    public static $arrLogLevels = array(
        self::LOG_LEVEL_NONE    => 'NONE',
        self::LOG_LEVEL_FATAL   => 'FATAL',
        self::LOG_LEVEL_WARNING => 'WARNING',
        self::LOG_LEVEL_NOTICE  => 'NOTICE',
        self::LOG_LEVEL_TRACE	=> 'TRACE',
        self::LOG_LEVEL_DEBUG   => 'DEBUG',
        self::LOG_LEVEL_ALL     => 'ALL',
    );

    protected $intLevel;
    protected $strLogFile;
    protected $intLogId;
    protected $intStartTime;

    private static $instance = null;

    private function __construct($arrLogConfig, $intStartTime)
    {
        $this->intLevel         = intval($arrLogConfig['intLevel']);
        $this->strLogFile		= $arrLogConfig['strLogFile'];
        $this->intLogId			= self::__logId();
        $this->intStartTime		= $intStartTime;
    }

	/**
	 * @return Log
	 */
	public static function getInstance()
	{
		if (self::$instance === null) {
			$intStartTime = defined('PROCESS_START_TIME') ? PROCESS_START_TIME : microtime(true) * 1000;
			self::$instance = new Log($GLOBALS['LOG'], $intStartTime);
		}

		return self::$instance;
	}

    /**
     * Write debug log
     *
     * @param string $str		Self defined log string
     * @param int $errno		errno to be write into log
     * @param array $arrArgs	params in k/v format to be write into log
     * @param int $depth		depth of the function be packaged
     */
    public static function debug($str, $errno = 0, $arrArgs = null, $depth = 0)
    {
        $log = Log::getInstance();
        return $log->writeLog(self::LOG_LEVEL_DEBUG, $str, $errno, $arrArgs, $depth + 1);
    }

    /**
     * Write trace log
     *
     * @param string $str		Self defined log string
     * @param int $errno		errno to be write into log
     * @param array $arrArgs	params in k/v format to be write into log
     * @param int $depth		depth of the function be packaged
     */
	public static function trace($str, $errno = 0, $arrArgs = null, $depth = 0)
    {
        $log = Log::getInstance();
        return $log->writeLog(self::LOG_LEVEL_TRACE, $str, $errno, $arrArgs, $depth + 1);
    }

    /**
     * Write notice log
	 * 
     * @param string $str		Self defined log string
     * @param int $errno		errno to be write into log
     * @param array $arrArgs	params in k/v format to be write into log
     * @param int $depth		depth of the function to be packaged
     */
    public static function notice($str, $errno = 0, $arrArgs = null, $depth = 0)
    {
        $log = Log::getInstance();
        return $log->writeLog(self::LOG_LEVEL_NOTICE, $str, $errno, $arrArgs, $depth + 1);
    }

    /**
     * Write warning log
     *
     * @param string $str		Self defined log string
     * @param int $errno		errno to be write into log
     * @param array $arrArgs	params in k/v format to be write into log
     * @param int $depth		depth of the function be packaged
     */
    public static function warning($str, $errno = 0, $arrArgs = null, $depth = 0)
    {
        $log = Log::getInstance();
        return $log->writeLog(self::LOG_LEVEL_WARNING, $str, $errno, $arrArgs, $depth + 1);
    }

    /**
     * Write fatal log
     *
     * @param string $str		Self defined log string
     * @param int $errno		errno to be write into log
     * @param array $arrArgs	params in k/v format to be write into log
     * @param int $depth		depth of the function be packaged
     */
    public static function fatal($str, $errno = 0, $arrArgs = null, $depth = 0)
    {
        $log = Log::getInstance();
        return $log->writeLog(self::LOG_LEVEL_FATAL, $str, $errno, $arrArgs, $depth + 1);
    }

    /**
     * Get logid for current http request
     * @return int
     */
    public static function logId()
    {
        return Log::getInstance()->intLogId;
    }

    /**
     * 
     * Write log content into log file
     * 
     * @param int $intLevel		Self defined log level
     * @param string $str		Self defined log string
     * @param int $errno		errno to be write into log
     * @param array $arrArgs	params in k/v format to be write into log
     * @param int $depth		depth of the function be packaged
     */
	public function writeLog($intLevel, $str, $errno = 0, $arrArgs = null, $depth = 0)
	{
		if ($intLevel > $this->intLevel || !isset(self::$arrLogLevels[$intLevel])) {
			return;
		}

		$strLevel = self::$arrLogLevels[$intLevel];

		$strLogFile = $this->strLogFile;
		if (($intLevel & self::LOG_LEVEL_WARNING) || ($intLevel & self::LOG_LEVEL_FATAL)) {
			$strLogFile .= '.wf';
		}

		$trace = debug_backtrace();
		if ($depth >= count($trace)) {
			$depth = count($trace) - 1;
		}
		$file = basename($trace[$depth]['file']);
		$line = $trace[$depth]['line'];

		$strArgs = '';
		if (is_array($arrArgs) && count($arrArgs) > 0) {
			foreach ($arrArgs as $key => $value) {
				$strArgs .= $key . "[$value] ";
			}
		}

        $intTimeUsed = microtime(true)*1000 - $this->intStartTime;

        $str = sprintf( "%s: %s [%s:%d] errno[%d] ip[%s] logId[%u] uri[%s] time_used[%d] %s%s\n",
                        $strLevel,
                        date('m-d H:i:s:', time()),
                        $file, $line, $errno,
                        self::getClientIP(),
                        $this->intLogId,
                        isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '',
                        $intTimeUsed, $strArgs, $str);

        return file_put_contents($strLogFile, $str, FILE_APPEND);
    }

	/**
	 * 
	 * Generate logId
	 */
	private static function __logId()
	{
		$arr = gettimeofday();
		return ((($arr['sec']*100000 + $arr['usec']/10) & 0x7FFFFFFF) | 0x80000000);
	}
	
	/**
	 * 
	 * Get client ip address
	 */
	public static function getClientIP()
	{
		if ($_SERVER["HTTP_X_FORWARDED_FOR"]) {
			$ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
		} elseif ($_SERVER["HTTP_CLIENT_IP"]) {
			$ip = $_SERVER["HTTP_CLIENT_IP"];
		} elseif ($_SERVER["REMOTE_ADDR"]) {
			$ip = $_SERVER["REMOTE_ADDR"];
		} elseif (getenv("HTTP_X_FORWARDED_FOR")) {
			$ip = getenv("HTTP_X_FORWARDED_FOR");
		} elseif (getenv("HTTP_CLIENT_IP")) {
			$ip = getenv("HTTP_CLIENT_IP");
		} elseif (getenv("REMOTE_ADDR")) {
			$ip = getenv("REMOTE_ADDR");
		} else {
			$ip = "0.0.0.0";
		}

		$pos = strpos($ip, ',');
		if ($pos > 0) $ip = substr($ip, 0, $pos);
		return trim($ip);
	}
	
}

